Skip to content

Valkyrie test harness - #58

Draft
laritakr wants to merge 30 commits into
mainfrom
valkyrie-test-harness
Draft

Valkyrie test harness#58
laritakr wants to merge 30 commits into
mainfrom
valkyrie-test-harness

Conversation

@laritakr

@laritakr laritakr commented Aug 10, 2026

Copy link
Copy Markdown

Summary

  • Rewrites hyrax-doi for Valkyrie and current Hyrax, so DOI minting works again — the gem has been broken since around Hyrax 3.
  • At this point, minting is something a person asks for; only updates to works that already have a DOI happen automatically.

Details

  • Draft PR: no CI run has happened yet, and nothing has been exercised against DataCite outside of stubs. Opening this to get the four-target matrix running.
  • 1.0.0 is a deliberate breaking change: ActiveFedora is dropped, hyrax >= 5.3, no compatibility shims. 0.3-stable carries the old API.
  • Identifiers moved to their own table, one row per identifier, each with its own state and sync history. A work can hold a DOI and a second identifier without either overwriting the other; the doi attribute is now a projection of the primary row.
  • Intent and observed state are separate. doi_status_when_public stays on the work as the depositor's intent; what the provider reports lives on the identifier record. A work marked findable sits at registered while private and becomes findable when it — or its embargo — does.
  • DataCite REST v2 replaces the MDS API, with state set by an explicit event on one idempotent PUT rather than emerging from the side effects of three calls plus a corrective delete.
  • Credentials are per-registrar-instance, resolved per call. The old class_attributes were process-wide, so concurrent Sidekiq threads serving different tenants could overwrite each other's credentials mid-flight.
  • bolognese is gone (28 transitive dependencies). Autofill now reads CSL JSON via doi.org content negotiation, which resolves for any registration agency rather than DataCite alone.
  • Review commit-by-commit — each of the 19 is green on koppie and allinson.

Bugs found and fixed along the way

  • solr_document.doi read doi_ssi, which nothing has ever written, so a DOI could never reach a show page.
  • The autofill handler used the jquery-ujs event signature while the stack ships rails-ujs, so the DOI was never sent — its feature spec had been pending since 0.3.
  • All three generators were broken: install raised on boot, add_to_work_type silently changed nothing on Valkyrie work types, and the migration generator raised before writing.
  • The _attribute_rows override replaced profile-driven rendering with a hardcoded Hyrax 2.9 field list, so a work's own metadata profile decided nothing about its show page.
  • Closes Upgrade to Github CI #53 — the CircleCI badge was the last remnant; the workflows themselves were already migrated.

Not in this PR

  • An admin configuration page.
  • Hyku and Enact integration. Hyku overrides _show_actions.html.erb four times, so the show-page mint button needs work there regardless.
  • Let engines add action buttons to a work's show page samvera/hyrax#7573 adds a show_actions_for seam so the mint button needs no glue in the host application. The gem guards for its absence, so the two can merge in either order.
  • Fix coverage reporting #38 (coverage reporting) is not fixed. Hyrax's spec_helper calls SimpleCov.root and SimpleCov.start for itself, so the reported 2.66% is 659/24695 of Hyrax's codebase, not this gem's ~2,000 lines.
  • Automatic doi minting. At this point, minting is something a person asks for.

laritakr and others added 19 commits August 1, 2026 16:19
The test suite now runs green against Hyrax's Valkyrie test apps in all
three persistence configurations: koppie (Postgres, flex off), allinson
(Postgres, flex on), and sirenia (Fedora metadata). CI runs each.

The submodule had been pinned to a Hyrax 2.9-era commit and never checked
out, so no test app existed. ActiveFedora-era specs are tagged and skipped
until their part of the Valkyrie port lands. The submodule tracks main
because the released 5.3.0 references a class it does not ship.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The gem no longer depends on bolognese, removing 28 transitive gems.
DataCite's REST API accepts JSON directly, so the XML crosswalk had no
remaining purpose, and DOI autofill moves to doi.org content negotiation.

Metadata serialization and autofill now raise NotImplementedError until
their replacements land. The dependency floors also move up to match what
Hyrax 5.3 already requires: Ruby 3.2 and Rails 6.1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Identifiers now live in their own table, one row per identifier per
resource, so a work can hold a DOI and a RAiD at once without either
overwriting the other. Each row tracks its own provider state, sync time,
and whether we minted it or someone supplied it.

Hyrax's identifier interface assumes a single overwritable attribute and a
bare string return, which leaves no room for state, errors, or additional
schemes. Registrars now return a result object, and providers are looked
up by scheme rather than hardcoded to DataCite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Work types now carry DOI fields as Valkyrie attributes, and a work type can
hold its DOI in an attribute other than doi via holds_doi_in. Solr indexing
covers the DOI, the depositor's intent, and the state the provider reports.

doi_status_when_public now records intent only; what DataCite currently says
lives on the identifier record, because state is provider-specific
vocabulary. Where a repository already declares the attribute the gem leaves
it alone, since redeclaring would make the gem unloadable there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds a rake task that installs the doi properties into the current m3
profile, and a validator that warns when a profile would leave DOIs
invisible on the show page or attached to no work type. The task creates a
new profile version rather than editing the current one, since saved works
pin their schema version to a row id.

Also adds a freyja test target: dassie with VALKYRIE_TRANSITION=true, the
only configuration with Wings loaded. It covers applications midway through
migrating off ActiveFedora, and immediately caught an rspec-rails API that
differs on the Rails 6.1 app.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Credentials now reach a registrar per instance, resolved through a store the
host application configures. The default reads them from the environment;
a multi-tenant host replaces it to source them per tenant. A connection test
confirms both that DataCite answers and that the credentials work, without
minting anything.

Credentials were class attributes, which are process-wide, so two Sidekiq
threads serving different tenants could overwrite each other mid-flight. The
store also describes its own fields, so a form can render itself from that
rather than hardcoding them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A serializer turns a work into DataCite REST v2 attributes. Which work field
feeds which DataCite field is read from datacite_mapping on m3 profile
properties, so an adopter can change it without a deploy, and it reports
which required fields a work still lacks.

Some values cannot be read from a field at all: a repository without a
creator field has to derive creators from its typed-role contributors. Those
come from a configured extractor, which takes precedence over the mapping.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Registering a DOI is now one idempotent request carrying the state we want.
DataCite's REST API takes an event — register, publish, or hide — so a work
intended to be findable while still private is hidden rather than published:
it resolves for anyone holding the DOI without being publicly indexed.

Registering returns the state DataCite reported, along with any field-level
errors, and missing required metadata is named before the request goes out.
The legacy MDS API needed three calls whose combined side effects determined
the state, including a delete that existed only to undo the other two.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Whether a work should get a DOI now lives in a policy object the registrar
consults, so a repository can limit minting to particular work types without
the registrar knowing about that choice. A work type qualifies by naming a
registrar, which is what including a scheme concern does.

The checks were three predicates inlined in the registrar, which left no way
to restrict minting without editing it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds a mint endpoint requiring edit permission on the work, and a helper that
decides whether to offer the button on a show page. Both minting endpoints
are POST and return JSON, since reserving an identifier has a side effect at
DataCite that a browser prefetch must not be able to trigger.

The gem also registers its own registrar now. Hyrax ships an empty registrar
hash and its generator only writes one into a host initializer, so a skipped
generator left minting silently unavailable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rewrites the DOI tab off inline jQuery onto a delegated vanilla-JS asset, and
adds the mint button as a show-page action. The tab's autofill handler used the
jquery-ujs event signature while the stack ships rails-ujs, so the DOI was
never sent; the missing-field warning looked for a hardcoded creator selector
that matches nothing in a repository without that field.

The engine now wires its own helpers. The tab previously appeared only if the
adopter ran the install generator, and its work-type check read class ancestry,
which a flexible-metadata profile does not touch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replaces the actor stack with a publisher listener that syncs metadata for
works that already have a DOI. Editing a work without one still mints nothing.

It also listens for permission changes, not just metadata saves: embargo and
lease release change visibility without saving metadata, so a work meant to
become findable would otherwise stay registered indefinitely after release.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Autofill now works: paste a DOI and the form populates from the metadata its
publisher registered. Reading is separate from minting, so a DOI recorded this
way is marked external and never has metadata pushed back to it.

Metadata comes from doi.org content negotiation as CSL JSON, so any
registration agency resolves rather than DataCite alone — a journal article's
CrossRef DOI is the common thing a depositor pastes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds a renderer that links a DOI to its resolver, and withholds draft DOIs —
a draft is reserved but does not resolve, so linking one gives readers a dead
link.

Drops a copy of Hyrax 2.9's attribute markup that the gem carried to add its
one row. Hyrax now builds that list from each field's own profile settings, so
the copy was quietly overriding every work type's configured display.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rewrites the remaining pre-Valkyrie specs against Valkyrie resources, or drops
them where a Valkyrie spec already covered the same ground. All of them were
excluded from every run, so none had executed since the port began, and each
referenced code this rewrite has since removed.

Two gaps came out of it: the form and presenter concerns had no live coverage
at all, and the shared examples the gem ships for adopters were still asserting
ActiveModel validations that a Valkyrie resource does not have. The suite now
runs those examples so they cannot drift again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Installing raised on boot from registrar settings that no longer exist. The
work-type generator looked for class declarations Valkyrie work types do not
have, so it reported success while changing nothing. The migration generator
raised before writing anything.

Installing now also creates the identifier table, rather than leaving a step an
adopter hits later as a missing-table error on first mint.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removes a helper module nothing includes any more, and which would misbehave
if something did: the engine prepends both helpers it bundles, so including it
would shadow Hyrax's own methods instead of wrapping them.

Also drops rubocop exclusions naming five deleted spec files, and corrects the
gemspec, which claimed MIT and packaged a license filename this repo has never
had. The license is Apache 2.0, as the LICENSE file, the README badge, and
every other Samvera project have it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rewrites the README around explicit minting, which is the behavior most likely
to surprise someone expecting the old automatic version, and adds a
configuration guide covering credential stores, minting policy, and the
flexible-metadata setup.

Drops two guides written for Hyrax 2.9 whose steps referenced code this
rewrite removed. Nothing linked to either.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Swaps the CircleCI badge for the GitHub Actions workflow. CircleCI still
serves an image for projects it no longer builds, so the old badge looked like
a passing build while reporting on nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes hyrax-doi for Valkyrie-only Hyrax (>= 5.3), reintroducing working DOI minting/sync with a new persistence model (one row per identifier) and updated DataCite REST v2 integration, plus a refreshed test harness across multiple Hyrax targets.

Changes:

  • Introduces Hyrax::DOI::PersistentIdentifier table-backed storage and keeps work.doi as a projection for indexing/display compatibility.
  • Replaces prior ActiveFedora/Bolognese-based flows with DOI.org CSL autofill, DataCite REST v2 registration results, credential-store based configuration, and publisher-driven sync jobs.
  • Adds extensive RSpec coverage, updated generators, docker-compose targets, and GitHub Actions matrix to validate multiple Hyrax configurations (koppie/allinson/sirenia/freyja).

Reviewed changes

Copilot reviewed 132 out of 135 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
spec/views/hyrax/base/form_doi_spec.rb Adds view spec coverage for DOI form partial behavior (buttons/radios/autofill attrs).
spec/support/persistent_identifier_table.rb Creates PID table in the test environment for model specs without running generators.
spec/spec_helper.rb Keeps ActiveFedora-tag exclusion filter for cross-target consistency.
spec/services/hyrax/doi/state_reachability_spec.rb Tests DataCite state reachability constraints.
spec/services/hyrax/doi/registration_result_spec.rb Tests the new registrar result object (success/changed/errors).
spec/services/hyrax/doi/recorded_identifier_spec.rb Tests recorded (non-minted) identifier schemes (ORCID/ROR).
spec/services/hyrax/doi/publisher_listener_spec.rb Tests publisher subscription and sync-enqueue rules.
spec/services/hyrax/doi/minting_policy_spec.rb Tests minting eligibility rules and registrar policy enforcement.
spec/services/hyrax/doi/flexible_schema_validators/doi_validator_spec.rb Tests DOI-related warnings emitted from flexible schema validator.
spec/services/hyrax/doi/flexible_schema_validator_integration_spec.rb Confirms decorator prepend is active inside Hyrax validator service.
spec/services/hyrax/doi/flexible_profile_installer_spec.rb Tests profile merge/versioning behavior for flexible metadata installs.
spec/services/hyrax/doi/doi_resolver_spec.rb Tests DOI.org CSL JSON resolution and parsing into Hyrax attributes.
spec/services/hyrax/doi/datacite_serializer_spec.rb Tests DataCite payload generation and required-field reporting.
spec/services/hyrax/doi/datacite_registrar_spec.rb Removes legacy registrar specs tied to old API/behavior.
spec/services/hyrax/doi/datacite_registrar_credentials_spec.rb Tests registrar credential resolution and ping behavior.
spec/services/hyrax/doi/credentials_spec.rb Tests credential object completeness, mode normalization, and redaction.
spec/services/hyrax/doi/credential_store_spec.rb Tests environment store behavior and thread-isolated credential fetching.
spec/services/bolognese/writers/hyrax_work_writer_spec.rb Removes specs for Bolognese-based roundtripping (dependency removed).
spec/services/bolognese/readers/hyrax_work_reader_spec.rb Removes specs for Bolognese-based crosswalk reading (dependency removed).
spec/routing/hyrax/doi/routes_spec.rb Adds routing specs for POST-only mint/draft and GET autofill.
spec/renderers/hyrax/renderers/doi_attribute_renderer_spec.rb Adds renderer spec ensuring DOI links and draft suppression.
spec/rails_helper.rb Updates fixtures config for rspec-rails 6/7 compatibility and removes AF cleaner hooks.
spec/presenters/hyrax/doi/doi_presenter_behavior_spec.rb Adds presenter behavior specs for DOI and state/intent separation.
spec/presenters/concerns/doi_presenter_behavior_spec.rb Removes legacy presenter shared-spec wiring.
spec/presenters/concerns/datacite_doi_presenter_behavior_spec.rb Removes legacy DataCite presenter shared-spec wiring.
spec/models/hyrax/doi/persistent_identifier_spec.rb Adds model spec coverage for PID validation, uniqueness, sync history.
spec/models/concerns/hyrax/doi/doi_projection_spec.rb Tests projection sync between PID record and work attribute.
spec/models/concerns/hyrax/doi/doi_collision_spec.rb Tests safe behavior when host predeclares DOI fields/types.
spec/models/concerns/hyrax/doi/doi_behavior_spec.rb Adds DOIBehavior spec coverage (regex, registrar defaults).
spec/models/concerns/hyrax/doi/doi_attribute_name_spec.rb Tests configurable attribute name for DOI projection/read/write.
spec/models/concerns/hyrax/doi/datacite_doi_behavior_spec.rb Tests DataCite concern behavior and shared examples alignment.
spec/models/concerns/doi_behavior_spec.rb Removes legacy DOIBehavior spec targeting ActiveFedora GenericWork.
spec/models/concerns/datacite_doi_behavior_spec.rb Removes legacy DataCite concern spec targeting ActiveFedora GenericWork.
spec/jobs/hyrax/doi/sync_doi_job_spec.rb Adds specs for sync job behavior (provider routing, missing work).
spec/jobs/hyrax/doi/register_doi_job_spec.rb Removes legacy job spec for AF-era register job.
spec/indexers/hyrax/doi/indexers/doi_indexer_spec.rb Adds specs for indexing projection, intent vs state, and custom attr name.
spec/helpers/work_show_helper_spec.rb Removes legacy helper spec tied to removed WorkShowHelper behavior.
spec/helpers/work_form_helper_spec.rb Removes legacy helper spec tied to old helper-injection approach.
spec/helpers/hyrax/doi/work_form_helper_spec.rb Adds helper specs for tab insertion and required-field selector generation.
spec/helpers/hyrax/doi/mint_button_helper_spec.rb Adds helper specs for mint button eligibility and show_actions_for integration.
spec/generators/migrations_generator_spec.rb Adds generator specs ensuring migration writes correctly and includes indices/version.
spec/generators/install_generator_spec.rb Updates install generator specs to validate initializer is inert and migration installed.
spec/forms/hyrax/doi/form_behavior_spec.rb Adds form behavior specs for DOI/DataCite form concerns on ResourceForm.
spec/forms/concerns/doi_form_behavior_spec.rb Removes legacy form concern spec targeting GenericWorkForm.
spec/forms/concerns/datacite_doi_form_behavior_spec.rb Removes legacy DataCite form concern spec targeting GenericWorkForm.
spec/fixtures/metadata.xml Removes legacy DataCite XML fixture.
spec/fixtures/datacite.json Removes legacy DataCite JSON fixture (Bolognese-era).
spec/fixtures/csl/datacite_dataset.json Adds CSL JSON fixture for DOI.org resolution tests.
spec/fixtures/csl/crossref_journal_article.json Adds CSL JSON fixture for DOI.org resolution tests.
spec/features/create_draft_doi_spec.rb Removes legacy JS feature spec based on old front-end/API expectations.
spec/features/autofill_spec.rb Removes legacy/pending JS feature spec tied to removed stack.
spec/controllers/hyrax_doi_controller_spec.rb Removes legacy controller spec (old formats/behavior).
spec/actors/hyrax/actors/doi_actor_spec.rb Removes legacy actor spec (actor itself removed).
lib/tasks/hyrax/doi_tasks.rake Adds rake task to install DOI properties into flexible metadata profiles.
lib/hyrax/doi/spec/shared_specs/solr_document/doi_behavior.rb Updates shared spec for SolrDocument DOI fields and adds doi_state checks.
lib/hyrax/doi/spec/shared_specs/doi_form_behavior.rb Updates shared form spec to reflect terms handling under new UI/tab approach.
lib/hyrax/doi/spec/shared_specs/doi_behavior.rb Updates shared model spec to reflect Valkyrie behavior and projection APIs.
lib/hyrax/doi/spec/shared_specs/datacite_doi_form_behavior.rb Updates shared DataCite form spec similarly to DOI form shared spec.
lib/hyrax/doi/flexible_schema_validator_service_decorator.rb Adds decorator to append DOI profile validation warnings.
lib/hyrax/doi/engine.rb Wires registrars, schema search path, helper prepends, and publisher subscription.
lib/hyrax/doi/configuration.rb Adds configuration object for providers, credential store, extractors, minting policy.
lib/hyrax/doi.rb Requires configuration and removes placeholder module content.
lib/generators/hyrax/doi/templates/db/migrate/create_hyrax_doi_persistent_identifiers.rb.erb Adds migration template for new PID table.
lib/generators/hyrax/doi/templates/config/initializers/hyrax-doi.rb Rewrites initializer template to use Hyrax::DOI.configure (optional config).
lib/generators/hyrax/doi/migrations_generator.rb Adds dedicated migration generator (with Rails migration version stamping).
lib/generators/hyrax/doi/install_generator.rb Updates install generator to write initializer + invoke migrations (no helper injection).
lib/generators/hyrax/doi/add_to_work_type_generator.rb Updates generator to inject concerns into Valkyrie work model/forms (no presenter injection).
hyrax-doi.gemspec Updates license, Ruby/Hyrax/Rails constraints, dependencies (remove bolognese, add faraday follow redirects).
Gemfile.koppie Adds test-app gemfile for koppie target.
Gemfile.dassie Cleans comment and keeps target gemfile.
Gemfile.allinson Adds test-app gemfile for allinson target.
Gemfile Pins erb ~> 4.0 for sprockets compatibility.
docs/IMPLEMENTATION.md Removes outdated Hyrax 2.9/ActiveFedora implementation guide.
docs/CONFIGURATION.md Adds new configuration documentation aligned to credential store/policy/flex profile install.
docker-compose-sirenia.yml Adds sirenia target compose for Fedora Valkyrie adapters.
docker-compose-koppie.yml Adds koppie target compose.
docker-compose-freyja.yml Updates freyja compose with transition mode and clearer naming/comments.
docker-compose-allinson.yml Adds allinson target compose.
config/routes.rb Makes draft reservation POST-only and adds POST mint route.
config/metadata/doi.yaml Adds schema loader config for doi and doi_status_when_public fields/indexing/view behavior.
config/locales/hyrax_doi.en.yml Adds new i18n strings for draft/autofill/mint UI and flexible-schema warnings.
app/views/hyrax/base/_show_action_mint_doi.html.erb Adds show-page mint action partial wired via helper seam.
app/views/hyrax/base/_attribute_rows.html.erb Removes hardcoded Hyrax 2.9-era override for show metadata rows.
app/services/hyrax/doi/registration_result.rb Adds richer registrar result type (identifier/state/changed/errors/response).
app/services/hyrax/doi/recorded_identifier.rb Adds recorded identifier validation/normalization/resolution (ORCID/ROR).
app/services/hyrax/doi/publisher_listener.rb Adds publisher listener to enqueue DOI sync when metadata/ACL updates happen.
app/services/hyrax/doi/ping_result.rb Adds ping result type for credential/service checks.
app/services/hyrax/doi/minting_policy.rb Adds policy object controlling mint eligibility and default state.
app/services/hyrax/doi/flexible_schema_validators/doi_validator.rb Adds flexible-schema DOI validator emitting warnings (not errors).
app/services/hyrax/doi/flexible_profile_installer.rb Adds service to create new flexible profile version including DOI properties.
app/services/hyrax/doi/doi_resolver.rb Adds DOI.org CSL resolver for autofill (follow redirects, parse/shape checks).
app/services/hyrax/doi/datacite_serializer.rb Adds serializer for DataCite REST v2 payloads, including profile mapping and extractors.
app/services/hyrax/doi/credentials.rb Adds credential data object with mode normalization and redaction.
app/services/hyrax/doi/credential_store.rb Adds credential store API plus environment-backed store and field schemas.
app/services/bolognese/writers/hyrax_work_writer.rb Removes Bolognese writer integration.
app/services/bolognese/readers/hyrax_work_reader.rb Removes Bolognese reader integration.
app/renderers/hyrax/renderers/doi_attribute_renderer.rb Adds DOI renderer resolving render_as: doi and suppressing drafts.
app/presenters/concerns/hyrax/doi/doi_presenter_behavior.rb Updates presenter to read multivalued Solr field and pass render options incl. state.
app/presenters/concerns/hyrax/doi/datacite_doi_presenter_behavior.rb Updates status logic to reflect observed provider state vs depositor intent.
app/models/hyrax/doi/persistent_identifier.rb Adds ActiveRecord model for identifier rows and sync history.
app/models/concerns/hyrax/doi/solr_document/doi_behavior.rb Updates SolrDocument attributes to read correct Solr keys and adds doi_state.
app/models/concerns/hyrax/doi/doi_behavior.rb Rewrites DOIBehavior for Valkyrie, projection, PID lookup, and configurable attr name.
app/models/concerns/hyrax/doi/datacite_doi_behavior.rb Updates DataCite concern to declare intent field and registrar name (Valkyrie).
app/jobs/hyrax/doi/sync_doi_job.rb Adds sync job to push updated metadata to provider for minted identifiers.
app/jobs/hyrax/doi/register_doi_job.rb Removes legacy register job.
app/indexers/hyrax/doi/indexers/doi_indexer.rb Adds indexer mixin for DOI fields, intent, and state.
app/helpers/hyrax/doi/work_show_helper.rb Removes legacy show helper used by prior attribute_rows override.
app/helpers/hyrax/doi/work_form_helper.rb Updates helper to add DOI tab under flex/non-flex and expose required-field selectors.
app/helpers/hyrax/doi/mint_button_helper.rb Adds helper to contribute show action and determine mint button eligibility.
app/helpers/hyrax/doi/helper_behavior.rb Removes legacy helper aggregation module.
app/forms/concerns/hyrax/doi/doi_form_behavior.rb Updates form concern to delegate DOI without forcing it into generic term lists.
app/forms/concerns/hyrax/doi/datacite_doi_form_behavior.rb Updates form concern to delegate intent field without adding to generic term lists.
app/controllers/hyrax/doi/hyrax_doi_controller.rb Rewrites controller actions to JSON API for draft/mint/autofill with updated auth.
app/actors/hyrax/actors/doi_actor.rb Removes legacy actor-based automatic minting behavior.
.ruby-version Sets repo Ruby version to 3.3.0.
.rubocop.yml Updates RSpec cop exceptions after spec suite restructuring.
.gitignore Ignores OS/editor artifacts and multiple app lockfiles.
.github/workflows/lint-test.yml Expands CI matrix to multiple docker-compose targets and parallel nodes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread app/controllers/hyrax/doi/hyrax_doi_controller.rb
Comment thread app/models/concerns/hyrax/doi/doi_behavior.rb
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

Test Results

   17 files  +   12     17 suites  +12   12m 50s ⏱️ + 8m 47s
  353 tests +  219    353 ✅ +  220  0 💤  - 1  0 ❌ ±0 
1 532 runs  +1 327  1 532 ✅ +1 328  0 💤  - 1  0 ❌ ±0 

Results for commit f574261. ± Comparison against base commit 842c11d.

This pull request removes 125 and adds 344 tests. Note that renamed tests count towards both.
spec.actors.hyrax.actors.doi_actor_spec ‑ Hyrax::Actors::DOIActor#create enqueues a job
spec.actors.hyrax.actors.doi_actor_spec ‑ Hyrax::Actors::DOIActor#create when the work is not DOI-enabled does not enqueue a job
spec.actors.hyrax.actors.doi_actor_spec ‑ Hyrax::Actors::DOIActor#update enqueues a job
spec.actors.hyrax.actors.doi_actor_spec ‑ Hyrax::Actors::DOIActor#update when the work implements registrar_name and registrar_opts enqueues a job
spec.actors.hyrax.actors.doi_actor_spec ‑ Hyrax::Actors::DOIActor#update when the work is not DOI-enabled does not enqueue a job
spec.controllers.hyrax_doi_controller_spec ‑ Hyrax::DOI::HyraxDOIController autofill JS format with invalid doi returns a failure
spec.controllers.hyrax_doi_controller_spec ‑ Hyrax::DOI::HyraxDOIController autofill JS format with valid doi returns autofill JS
spec.controllers.hyrax_doi_controller_spec ‑ Hyrax::DOI::HyraxDOIController autofill authorization when unauthorized redirects to new user login
spec.controllers.hyrax_doi_controller_spec ‑ Hyrax::DOI::HyraxDOIController create_draft_doi JS format returns a JS with the new DOI
spec.controllers.hyrax_doi_controller_spec ‑ Hyrax::DOI::HyraxDOIController create_draft_doi JS format with datacite error returns a failure
…
spec.controllers.hyrax.doi.mint_actions_spec ‑ Hyrax::DOI::HyraxDOIController GET #autofill distinguishes a resolver outage from a missing DOI
spec.controllers.hyrax.doi.mint_actions_spec ‑ Hyrax::DOI::HyraxDOIController GET #autofill refuses a user who cannot deposit
spec.controllers.hyrax.doi.mint_actions_spec ‑ Hyrax::DOI::HyraxDOIController GET #autofill reports a DOI that does not resolve
spec.controllers.hyrax.doi.mint_actions_spec ‑ Hyrax::DOI::HyraxDOIController GET #autofill returns the attributes a form can apply
spec.controllers.hyrax.doi.mint_actions_spec ‑ Hyrax::DOI::HyraxDOIController POST #create_draft_doi records the reservation so it is not an orphan
spec.controllers.hyrax.doi.mint_actions_spec ‑ Hyrax::DOI::HyraxDOIController POST #create_draft_doi refuses a user who cannot deposit
spec.controllers.hyrax.doi.mint_actions_spec ‑ Hyrax::DOI::HyraxDOIController POST #create_draft_doi refuses when DOI minting is switched off
spec.controllers.hyrax.doi.mint_actions_spec ‑ Hyrax::DOI::HyraxDOIController POST #create_draft_doi reports DataCite refusing without raising
spec.controllers.hyrax.doi.mint_actions_spec ‑ Hyrax::DOI::HyraxDOIController POST #create_draft_doi returns the minted DOI as JSON
spec.controllers.hyrax.doi.mint_actions_spec ‑ Hyrax::DOI::HyraxDOIController POST #mint does not record anything when the registrar failed
…

♻️ This comment has been updated with latest results.

laritakr and others added 4 commits August 10, 2026 10:22
Answers a mint request for an unknown work with a JSON 404 instead of raising
into an HTML 500 the client cannot read, and writes a single value where a work
type's DOI field will not accept an array.

Gives each CI shard its own Docker project. Four shards of one app shared a set
of volumes and raced to create directories inside them, so the containers never
started and no specs ran. Also clears 71 rubocop offenses that only the bundled
version reports.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The web and worker containers share two volumes and both create the same
directories in them at boot, so starting the pair raced and one died before any
specs could run. Specs enqueue with the test adapter and need no worker.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Lowers the Hyrax floor to 5.2. The 5.3.0 version bump lives on a release commit
that was never merged back, so Hyrax main ships the flexible metadata stack
while still declaring 5.2.0 — and a 5.3 floor made the gem unresolvable for
every application tracking main, Hyku included.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The generated migration declared CreateHyraxDoiPersistentIdentifiers, but the
engine registers DOI as an acronym, so Rails camelizes the filename to
CreateHyraxDOIPersistentIdentifiers and db:migrate raised NameError. The
generator's own spec had the wrong spelling too, so nothing caught it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@laritakr
laritakr force-pushed the valkyrie-test-harness branch 2 times, most recently from 93ebe1d to 56c0693 Compare August 10, 2026 21:27
The DOI tab now records what a depositor enters. It offers three situations --
mint nothing, record a DOI the work already has, or mint a new one -- and only
the chosen one's controls apply, so the panels no longer submit each other's
values. Minting writes an identifier record that keeps the work's DOI in step
for indexing and display, and a reserved draft is recorded before any work
exists to hold it. The tab also no longer raises on a work type whose class does
not declare the DOI attribute, which had stopped the create form opening at all.

The tab collected values that were then discarded: the form concerns delegated
readers to the work but declared no writable property, so ResourceForm dropped
both DOI fields on submit, and nothing created a PersistentIdentifier row -- the
sync job returns early without one, and a reserved DOI existed only at DataCite.
The shared form examples asserted delegation rather than round-tripping, which
is what let the missing writer stay green. Reconciling the submitted mode reads
params without to_h, which raises on the unpermitted parameters a controller
passes.
The autofill, reserve-a-DOI, and mint buttons now build their URLs from the
mounted engine, so they reach the application's own mount point instead of a
path nothing serves. Saving a work with a minting choice no longer fails on
`undefined method 'embargo' for an instance of String`. The install generator
reports when it cannot add the mount rather than claiming success, and the
README says how to add it by hand.

Three faults with one shape -- something worked in the suite but not in an
application. Engine.routes.url_helpers builds paths against the engine's own
route set and ignores the host's mount point; only feature specs mounted the
engine, so no view spec could catch it. The form's sync returned the value an
attribute writer hands back rather than the resource, which Hyrax's save step
then asks for an embargo. The engine is now mounted for the whole suite, and
the shared form examples assert what sync returns.
"Reserve a DOI now" now shows the identifier in a field of its own beside the
button, with a copy button, and saves it with the work. Reserving a DOI early
exists so it can be pasted into a document before that document is uploaded,
so the depositor has to be able to read and copy it.

The reservation was written into the existing-DOI input, which sits in a
different mode panel: the depositor saw nothing but a disabled button, and
because the minting mode discards that panel's value, the DOI was created at
DataCite and then dropped when the work saved. The copy button falls back to
selecting the text where navigator.clipboard is unavailable.
A work saving with a DOI reserved before it existed now claims that identifier
record, so DataCite receives the work's metadata instead of keeping a draft
that has nothing but a prefix. Describing a DOI a work already holds no longer
requires a minting choice, while creating one still does -- a work type an
operator excluded from minting is never touched either way.

The reservation was recorded before the work existed and so carried no
resource_id; nothing linked the two afterwards, so no sync ever ran and the
orphan sweep would have counted a DOI that was in use. Hyrax::DOI.config is
also reset between examples now: specs that set a restrictive policy leaked
into everything after them, which is what four order-dependent failures here
turned out to be.
Reserving a DOI now sets the work's minting intent to draft, so the work agrees
it holds a reserved identifier. Nothing submits until the reservation actually
succeeds, and a status the depositor picks afterwards still wins.

A reserved DOI with a blank intent left the work holding a real identifier
while claiming to want none. doi_status reported nothing, so the renderer could
not tell a draft from a resolvable DOI and would have linked one that does not
resolve; the show-page mint button requires an intent, so it was hidden, which
left no route to promote the DOI at all.
Choosing Draft, Registered, or Findable on the deposit form now creates the DOI
when the work is saved, and records the identifier so later edits keep DataCite
current. A work type the minting policy excludes still mints nothing, and
editing a work that asked for no DOI still creates none.

The intent was recorded and nothing acted on it: the listener enqueued a sync
only for a work that already had an identifier record, and the job returned
early without one, so no DOI was created and no result was written back. A
reserved DOI also moves to its own form parameter -- it shared doi[] with the
existing-DOI field, so the reconciliation that drops a value left behind in an
unselected panel could not tell the two apart and discarded an identifier that
already existed at DataCite.
A registered or findable DOI is only created when the work carries the metadata
DataCite requires. Blank required fields are named for the depositor to fill,
and the deposit form says plainly that no DOI will be created until they are.
A sync job also mints at most one DOI however many times it runs.

The serializer substituted DataCite's :unav sentinel for a blank creator or
publisher, the current year for a missing publication year, and Other for a
missing resource type -- then reported those same fields as absent, so the
registrar refused to mint and the placeholders were discarded while the form
promised they would be sent. Minting decided from the absence of an identifier
record, which is written only after the provider replies, so a duplicate
enqueue or a retry each saw nothing and each created a permanent identifier.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upgrade to Github CI

2 participants